﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.DirectoryServices;
    using System.Diagnostics;
    using System.Threading; 
    #endregion

    /// <summary>
    /// The NTDSSettings object is a child of a Server object within an AD Site object
    /// The NTDSSettings object is also known as nTDSDSA.
    /// Each DC in a forest has an nTDSDSA object in the config NC. 
    /// </summary>
    [Serializable]
    public class NTDSSettings : ADBaseObjectProperties {

        #region Members
        #region Fields
        public static readonly new string[] AttributesToGet;

        public static readonly string ObjectCategoryShortName = "nTDSDSA"; 
        #endregion

        public string DMDLocation { get; private set; }

        public IReadOnlyList<string> HasMasterNCs { get; private set; }

        public IReadOnlyList<string> HasPartialReplicaNCs { get; private set; }

        public InstanceTypeFlags InstanceType { get { return instanceType; } }
        private InstanceTypeFlags instanceType;

        public Guid InvocationId { get; private set; }

        public MSDSBehaviorVersionDefinition MSDSBehaviorVersion { get { return msDSBehaviorVersion; } }
        private MSDSBehaviorVersionDefinition msDSBehaviorVersion;

        public IReadOnlyList<string> MSDSHasFullReplicaNCs { get; private set; }

        public IReadOnlyList<NTDSConnection> NTDSConnections {
            get {
                if (ntdsConnections == null) {
                    this.GetNTDSConnections();
                }

                return ntdsConnections;
            }
        }
        private IReadOnlyList<NTDSConnection> ntdsConnections;

        public NTDSSettingsOptionsFlags Options { get { return options; } }
        private NTDSSettingsOptionsFlags options;
        #endregion

        #region Constructors
        static NTDSSettings() {
            var attributesToGet = new List<string>();
            attributesToGet.AddRange(new string[]
            {
                "dMDLocation",
                "hasMasterNCs",
                "hasPartialReplicaNCs",
                "invocationId",
                "msDS-Behavior-Version",
                "msDS-HasDomainNCs",
                "msDS-hasFullReplicaNCs",
                "msDS-hasInstantiatedNCs",
                "msDS-hasMasterNCs",
                "options",
                "schedule"
            });

            attributesToGet.AddRange(ADBaseObjectProperties.AttributesToGet);

            AttributesToGet = attributesToGet.Distinct().ToArray();
            attributesToGet = null;
        }
        public NTDSSettings() {
            this.Initialize();
        } 

        public NTDSSettings(DirectoryEntry directoryEntry, bool destroyInputObject = true)
            : base(directoryEntry, destroyInputObject) {
                if (this.GetType().Name == typeof(NTDSSettings).Name) {
                this.Attributes = null;
            }
        }

        public NTDSSettings(SearchResult result, bool destroyInputObject = true)
            : base(result, destroyInputObject) {
                if (this.GetType().Name == typeof(NTDSSettings).Name) {
                this.Attributes = null;
            }
        }

        public NTDSSettings(List<string> ldifOutput)
            : base(ldifOutput) {
                if (this.GetType().Name == typeof(NTDSSettings).Name) {
                this.Attributes = null;
            }
        }
        #endregion

        #region Methods
        
        public void GetNTDSConnections() {
            var filter = string.Format("(objectCategory={0})", NTDSConnection.ObjectCategoryShortName);
            ntdsConnections = ADDataAccess.GetADObjectProperties<NTDSConnection>(                
                this.DN, NTDSConnection.AttributesToGet, filter, PartitionScope.LocalPartition);

            if (ntdsConnections == null) ntdsConnections = new List<NTDSConnection>();
        }

        protected override void Initialize() {
            base.Initialize();

            this.DMDLocation = string.Empty;
            instanceType = InstanceTypeFlags.Undefined;
            msDSBehaviorVersion = MSDSBehaviorVersionDefinition.WIN2008R2;
            options = NTDSSettingsOptionsFlags.Undefined;

            this.MSDSHasFullReplicaNCs = new List<string>();
            this.HasMasterNCs = new List<string>();
            this.HasPartialReplicaNCs = new List<string>();
        }

        protected override void ProcessResults() {

            try {
                base.ProcessResults();

                if (this.Attributes.ContainsKey("dMDLocation")) {
                    this.DMDLocation = this.Attributes["dMDLocation"][0].ToString();
                }
                this.HasPartialReplicaNCs = ADDataAccess.GetMultiValuedAttribute<string>("hasPartialReplicaNCs", this.Attributes);
                if (this.Attributes.ContainsKey("instanceType")) {
                    Enum.TryParse<InstanceTypeFlags>(this.Attributes["instanceType"][0].ToString(), result: out instanceType);
                }
                if (this.Attributes.ContainsKey("invocationId")) {
                    this.InvocationId = new Guid((byte[])this.Attributes["invocationId"][0]);
                }
                if (this.Attributes.ContainsKey("msDS-Behavior-Version")) {
                    Enum.TryParse<MSDSBehaviorVersionDefinition>(this.Attributes["msDS-Behavior-Version"][0].ToString(), result: out msDSBehaviorVersion);
                }
                this.MSDSHasFullReplicaNCs = ADDataAccess.GetMultiValuedAttribute<string>("msDS-hasFullReplicaNCs", this.Attributes);
                if (this.Attributes.ContainsKey("options")) {
                    Enum.TryParse<NTDSSettingsOptionsFlags>(this.Attributes["options"][0].ToString(), result: out options);
                }

                if (this.Attributes.ContainsKey("msDS-hasMasterNCs")) {
                    this.HasMasterNCs = ADDataAccess.GetMultiValuedAttribute<string>("msDS-hasMasterNCs", this.Attributes);
                }
                else {
                    this.HasMasterNCs = ADDataAccess.GetMultiValuedAttribute<string>("hasMasterNCs", this.Attributes);
                }

                this.IsValid = !string.IsNullOrWhiteSpace(this.DN)
                    && this.ObjectClass.Any(x => string.Equals(x, ObjectCategoryShortName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception e) {
                Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} Error: {3}", DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId, CustomExtensions.CurrentMethodName(), e.VerboseExceptionString()));
                throw;
            }
        }

        [DebuggerStepThroughAttribute]
        public override string ToString() {
            string details = this.DN;
            if (string.IsNullOrWhiteSpace(details)) details = "N/A";
            return details;
        } 
        #endregion
    }
}
